﻿# elenkProbaja
A small validation library for .NET that uses a fluent interface 
and lambda expressions for building validation rules. 

*elenkProbaja (Hugarian: vivid Trial - Derived Meaning: fluent test)* 
## Creating Validators
```csharp
using elenkProbaja;

public class AccountValidator: AbstractValidator<Account> {
  public CustomerValidator() {
    //Basic Rule
    RuleFor(account => account.CompanyId).NotEmpty();
    //Chaining validators
    RuleFor(account => account.AccountName).NotEmpty().WithMessage("Please specify a Account Name");
    RuleFor(account => account.Status).NotEqual("C").When(account => account.ActivationDate > 1.Years().FromNow());
    RuleFor(account => account.Type).Equal("A");
    RuleFor(account => account.AccountNumber).Must(BeAValidAccountNumber).WithMessage("Please specify a valid Account Number");
  }

  private bool BeAValidAccountNumber(string accountNumber) {
    // custom postcode validating logic goes here
  }
}

Account account = new Account();
AccountValidator validator = new AccountValidator();
// A Validate method returns a validation result object that contains two properties: (IsValid) - whether validation suceeded, (Errors) a collection of failure details 
ValidationResult results = validator.Validate(account);

if(! results.IsValid) {
  foreach(var failure in results.Errors) {
    Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
  }
}
```

### Throwing exceptions
Instead of returning a ValidationResult, you can alternatively tell elenkProbaja to throw an exception if validation fails by using the ValidateAndthrow method:
```csharp
Account account = new Account();
AccountValidator validator = new AccountValidator();
validator.ValidateAndThrow(account);
```
### Complex Properties
Validators can be re-used for complex properties. For example, imagine you have two classes, Account and AccountRoles
```csharp
public class AccountRoleValidator : AbstractValidator<AccountRole> {
  public AccountRoleValidator() {
    RuleFor(acctRole => acctRole.RoleValue).NotNull();
    //etc
  }
```
You can then re-use the AccountRoleValidator in the AccountValidator definition:
```csharp
public class AccountValidator : AbstractValidator<Account> {
  public AccountValidator() {
    RuleFor(account => account.AccountName).NotNull();
    RuleFor(account => account.AccountRule).SetValidator(new AccountRoleValidator())
  }
}
```
### Collections
Validators can also be re-used on properties that contain collections of other objects. For example, imagine a Company object that has a collection of Accounts. (NOTE:When the validator is executed, the error messages will reflect the placement of the order object within the collection)
```csharp
public class AccountValidator : AbstractValidator<Account> {
  public AccountValidator() {
    RuleFor(account => account.AccountName).NotNull();
    RuleFor(account => account.AccountRule).SetValidator(new AccountRoleValidator())
  }
}
```
the above validator can be used within the CompanyValidator definition:
```csharp
public class CompanyValidator : AbstractValidator<Company> {
    public CompanyValidator() {
        RuleFor(x => x.Accounts).SetCollectionValidator(new AccountValidator());
    }
}
var validator = new CompanyValidator();
var results = validator.Validate(company);
```
### RuleSets
RuleSets allow you to group validation rules together which can be executed together as a group whilst ignoring other rules:
For example, let’s imagine we have 3 properties on a Users object (UserId, Lastname and Firstname) and have a validation rule for each. We could group the Lastname and Firstname rules together in a “Names” RuleSet:
```csharp
public class UserValidator : AbstractValidator<User> {
  public UserValidator() {
     RuleSet("Names", () => {
        RuleFor(x => x.Firstname).NotNull();
        RuleFor(x => x.Lastname).NotNull();
     });

     RuleFor(x => x.UserId).NotEqual(0);
  }
}
```
Here the two rules on Firstname and Lastname are grouped together in a “Names” RuleSet. We can invoke only these rules by passing a ruleSet parameter to the Validate extension method (note that this must be a named parameter as this overload has several options available).
```csharp
var validator = new UserValidator();
var user = new User();
var result = validator.Validate(user, ruleSet: "Names");
```
This allows you to break down a complex validator definition into smaller segments that can be executed in isolation.  Additionally, you can execute multiple rulesets by using a comma-separated list of strings:
```csharp
validator.Validate(user, ruleSet: "Names, MyRuleSet ,SomeOtherRuleSet")
```
You can also include rules not in any ruleset by specifying a ruleset of "default":
```csharp
validator.Validate(user, ruleSet: "default, MyRuleSet")
```
This would execute rules in the MyRuleSet set, and those rules not in any ruleset.

## Built In Validators
elenkProbaja has several built-in validators. The error message for each validator can contain special placeholders that will be filled in when the error message is constructed. The following is a list of built in validators:

#### NotNull
```csharp
RuleFor(user => user.Lastname).NotNull();
```
#### NotEmpty
```csharp
RuleFor(user => user.Lastname).NotEmpty();
```
#### NotEqual
```csharp
RuleFor(user => user.Lastname).NotEqual("Foo"); 
//Not equal to another property 
RuleFor(user => user.Lastname).NotEqual(user => user.Firstname);
```
#### Equal
```csharp
RuleFor(user => user.Lastname).Equal("Foo"); 
// Equal to another property 
RuleFor(user => user.Lastname).Equal(user => user.Firstname);
```
#### Length 
(NOTE: Only valid on string properties.)
```csharp
RuleFor(account => account.AccountNumber.Length(1, 250); //must be between 1 and 250 chars (inclusive)
```
#### LessThan 
```csharp
RuleFor(account => account.CurrentBalance).LessThan(100);
//Less than another property
RuleFor(account => account.CurrentBalance).LessThan(user => user.MaxCreditLimit);
```
#### LessThanOrEqual
```csharp
RuleFor(account => account.CurrentBalance).LessThanOrEqual(100);
// Less than another property
RuleFor(account => account.CurrentBalance).LessThanOrEqual(user => user.MaxCreditLimit);
```
#### GreaterThan
```csharp
RuleFor(account => account.CurrentBalance).GreaterThan(100);
// Less than another property
RuleFor(account => account.CurrentBalance).GreaterThan(user => user.MaxCreditLimit);
```
#### GreaterThanOrEqual
```csharp
RuleFor(account => account.CurrentBalance).GreaterThanOrEqual(100);
// Less than another property
RuleFor(account => account.CurrentBalance).GreaterThanOrEqual(user => user.MaxCreditLimit);
```
#### Predicate AKA "Must" 
(NOTE: There is an additional overload for Must that also accepts an instance of the parent object being validated. This can be useful if you want to compare the current property with another property from inside the predicate)
```csharp
RuleFor(user => user.Lastname).Must("Foo");
RuleFor(user => user.Lastname).Must((user, firstname) => firstname != user.Firstname);
```
#### Regular Expression
```csharp
RuleFor(user => user.Lastname).Mathces("some regex here");
```
#### Email
```csharp
RuleFor(user => user.Email).EmailAddress();
```
### Overriding Default Error
You can override the default error message for a validator by calling the WithMessage method on a validator definition.
```csharp
RuleFor(user => user.LastName).NotNull().WithMessage("Please ensure that you have entered your LastName");
```
Note that custom error messages can contain placeholders for special values such as '{PropertyName}' - which will be replaced in this example with the name of the property being validated.
```csharp
RuleFor(user =>  user.LastName).NotNull().WithMessage("Please ensure you have entered your {PropertyName}");
```
#### Configuring Error Message Parameters
As specified in the example above, the message can contain placeholders for special values such as {PropertyName}, which will be replaced with a specified value. 
Each type of built-in validator has its own list of placeholders which are supported by it. The placeholders are used in all validators: 
{PropertyName} = The name of the property being validated
{PropertyValue} = The value of the property being validated These include the predicate validator ('Must' validator), the email and the regex validators.

Used in comparison validators: *(Equal, NotEqual, GreaterThan, GreaterThanOrEqual, etc.)*
{ComparisonValue} = Value that the property should be compared to

Used only in the Length validator:
{MinLength} = Minimum length
{MaxLength} = Maximum length
{TotalLength} = Number of characters entered
```csharp
//Using static values in a custom message:
RuleFor(user =>  user.LastName).NotNull().WithMessage("This message references some static values: {0} {1}", "hello", 5);
//Result would be "This message references some static values: hello 5"

//Referencing other property values:
RuleFor(user =>  user.LastName)
  .NotNull()
  .WithMessage("This message references some other properties: FirstName: {0} Status: {1}", 
    user =>  user.FirstName, 
    user =>  user.Status
  );
//Result would be: "This message references some other properties: FirstName: Jason Status: A"
```
#### Overriding Default Property Names
The default validation error messages contain the property name being validated. For example, if you were to define a validator like this:
```csharp
RuleFor(user => user.LastName).NotNull();
```
The default error message would be 'LastName' must not be empty. Although you can override the entire error message by calling WithMessage, you can also replace just the property name by calling WithName:
```csharp
RuleFor(user => user.LastName).NotNull().WithName("Last Name");
```
Now the error message would be 'Last Name' must not be empty.
Additionally, elenkProbaja will respect the use of the DisplayName and Display attributes for generating the property's name within error messages:
```csharp
public class User {
  [Display(Name="Last name")]
  public string LastName { get; set; }
}
```
### Specifying conditional When/Unless
The 'When' and 'Unless' methods can be used to specify conditions that control when the rule should execute.
```csharp
RuleFor(account => account.CurrentBalance).GreaterThan(0).When(account => account.IsPreferred);
```
The 'Unless' method is simply the opposite of 'When'.
If you need to specify the same condition for multiple rules then you can call the top-level 'When' method instead of chaining the 'When' call at the end of the rule:
```csharp
When(account => account.IsPreferred, () => {
   RuleFor(account => account.CurrentBalance).GreaterThan(0);
   RuleFor(account => account.AccountNumber).NotNull();
});
```
The above condition will be applied to both rules.
### Cascade Mode
You can set the cascade mode to customise how elenkProbaja executes chained validators when a particular validator in the chain fails. Imagine you have two validators defined as part of a single rule definition, a NotNull validator and a NotEqual validator:
```csharp
RuleFor(x => x.LastName).NotNull().NotEqual("foo");
```
This will first check whether the LastName property is not null and then will check if it's not equal to the string "foo". If the first validator (NotNull) fails, then the call to NotEqual will still be invoked. This can be changed by specifying a cascade mode of StopOnFirstFailure:
```csharp
RuleFor(x => x.LastName).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
```
Now, if the NotNull validator fails then the NotEqual validator will not be executed. This is particularly useful if you have a complex chain where each validator depends on the previous validator to succeed. The two cascade modes are:
- Continue (the default) - always invokes all validators in a rule definition
- StopOnFirstFailure - stops executing a rule as soon as a validator fails
As well as being set at the rule level, the cascade mode can also be set globally for all validators, or for all the rules in a particular validator class.
To set the cascade mode globally, you can set the CascadeMode property on the static ValidatorOptions class during your application's startup routine: (NOTE: This can then be overridden by individual validator classes or by individual rules)
```csharp
ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;
```
To set the cascade mode for all rules inside a single validator class, set the CascadeMode property on AbstractValidator
```csharp
public class AccountRoleValidator : AbstractValidator<AccountRole> {
  public AccountRoleValidator() {
    //First Set the caascade mode before adding test rules
    CascadeMode = CascadeMode.StopOnFirstFailure;
    RuleFor(acctRole => acctRole.RoleValue).NotNull(); 
  }
```
## Custom Validators
elenkProbaja allows two ways of creating custom validators. The first is to create a custom property validator, the second is to make use of the Custom method on PropertyValidator.
### Custom Property Validator
To write a custom property validator you will need to inherit from the abstract PropertyValidator class. For example, imagine you want to create a validator that ensures that a list has fewer than 100 items. You could create the validator like this:
```csharp
using System.Collections.Generic;
using elenkProbaja.validators;
public class ListMustContainFewerThanHundredItemsValidator<T> : PropertyValidator {

    public ListMustContainFewerThanHundredItemsValidator() 
        : base("Property {PropertyName} contains more than 100 items!") {

    }

    protected override bool IsValid(PropertyValidatorContext context) {
        var list = context.PropertyValue as IList<T>;
        if(list != null && list.Count >= 100) {
            return false;
        }
        return true;
    }
}
```
When you inherit from PropertyValidator you must override the IsValid method. This method takes a PropertyValidatorContext object and should return a boolean indicating whether validation succeeded.
The PropertyValidatorContext object passed into the Validate method contains several properties:
Instance - the object being validated
PropertyDescription - the name of the property (or alternatively a custom name specifed by a call to WithName
PropertyValue - the value of the property being validated
Member - the MemberInfo describing the property being validated.

NOTE: that the error message to use is specified in the constructor. The simplest way to define your error message is to use the string (see example) but you can also used localized error messages by specifying either a resource type and resource name or a lambda expression. 
```csharp
public class CompanyValidator : AbstractValidator<Company> {
    public CompanyValidator() {
       RuleFor(company => company.Accounts).SetValidator(new ListMustContainFewerThanHundredItemsValidator<Account>());
    }
}
```
Alternatively, you could define an extension method which can then be chained like any other validator.
```csharp
public static class MyValidatorExtensions {
   public static IRuleBuilderOptions<T, IList<TElement>> MustContainFewerThanHundredItems<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder) {
      return ruleBuilder.SetValidator(new ListMustContainFewerThanHundredItemsValidator<TElement>());
   }
}

public class CompanyValidator : AbstractValidator<Company> {
    public CompanyValidator() {
       RuleFor(company => company.Accounts).MustContainFewerThanHundredItems();
    }
}
```
### AbstractValidator.Custom
Another approach to using custom validators is to make use of the Custom method defined on AbstractValidator. Using the Custom method, the above example could be written like this:
```csharp
public class CompanyValidator : AbstractValidator<Company> {
    public CompanyValidator() {
       Custom(company => { 
           return company.Accounts.Count >= 100 
              ? new LitValidationFailure("Accounts", "More than 99 accounts are not allowed.")
              : null; 
       }); 
    }
}
```
NOTE: 'Custom' is really only designed for complex validation scenarios. The majority of the time the same result can be achieved with much cleaner code by using the PredicateValidator which also allows you to easily write custom validation logic while still maintaining the in-line method chaining. 
```csharp
public class CompanyValidator : AbstractValidator<Company> {
   public CompanyValidator() {
      RuleFor(company => company.Accounts).Must(HaveFewerThanHundredAccounts).WithMessage("More than 99 accounts are not allowed");
   }

   private bool HaveFewerThanHundredAccounts(IList<Account> accounts) {
      return accounts.Count < 100;
   }
}
```
## elenkProbaja and IoC
When using elenkProbaja's ASP.NET MVC integration you may wish to use an Inversion of Control container to instantiate your validators. This can be achieved by writing a custom Validator Factory.
The IValidatorFactory interface defines the contract for validator factories.
```csharp
public interface IValidatorFactory {
  IValidator<T> GetValidator<T>();
  IValidator GetValidator(Type type);
}
```
Instead of implementing this interface directly, you can inherit from the ValidatorFactoryBase class which does most of the work for you. When you inherit from ValidatorFactoryBase you should override the CreateInstance method. In this method you should call in to your IoC container to resolve an instance of the specified type or return null if it does not exist (many containers have a "TryResolve" method that will do this automatically).
```csharp
public class ValidatorFactory : ValidatorFactoryBase {
    public override ILitValidator CreateInstance(Type validatorType) {
        return ObjectFactory.TryGetInstance(validatorType) as ILitValidator;
    }
}
```
You can then use this validator factory anywhere that requires an IValidatorFactory instance (eg the MVC integration)